package com.foodev.xgwt.sourcejar;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;

/**
 * This goal copies the sources to target/gwtsources and filters out the unwanted code before making a jar of them.
 * 
 * @version $Id$
 * @goal source-jar
 * @phase package
 * @execute phase="generate-sources"
 */
public class GWTSourceJarMojo extends AbstractSourceJarMojo {

    /**
     * List of files to include. Specified as fileset patterns which are relative to the input directory whose contents
     * is being packaged into the JAR.
     * 
     * @parameter
     */
    private String[] includes;

    /**
     * List of files to exclude. Specified as fileset patterns which are relative to the input directory whose contents
     * is being packaged into the JAR.
     * 
     * @parameter
     */
    private String[] excludes;

    @Override
    @SuppressWarnings("unchecked")
    protected List getSources(MavenProject p) throws MojoExecutionException {

        File output = new File(outputDirectory, getClassifier());

        if (output.exists()) {
            try {
                FileUtils.forceDelete(output);
            } catch (IOException ioe) {
                throw new MojoExecutionException("Failed to delete output dir: " + ioe.getMessage(), ioe);
            }
        }
        output.mkdirs();

        filterCopySourceFiles(p, output);

        return Arrays.asList(output.getAbsolutePath());

    }

    @SuppressWarnings("unchecked")
    private void filterCopySourceFiles(MavenProject p, File output) throws MojoExecutionException {

        List sources = p.getCompileSourceRoots();

        Iterator iter = sources.iterator();

        DirectoryScanner scanner = new DirectoryScanner();
        scanner.setExcludes(excludes);
        scanner.setIncludes(includes);
        scanner.addDefaultExcludes();

        try {

            while (iter.hasNext()) {

                String s = (String) iter.next();

                File sourceDir = new File(s);

                scanner.setBasedir(sourceDir);

                scanner.scan();

                String[] files = scanner.getIncludedFiles();

                for (String f : files) {

                    filterCopy(f, sourceDir, output);

                }

            }

        } catch (IOException ioe) {

            throw new MojoExecutionException("Error filtering file: " + ioe.getMessage(), ioe);

        }

    }

    private void filterCopy(String relPath, File sourceDir, File output) throws IOException {

        String relDir = FileUtils.dirname(relPath);

        File outputDir = new File(output, relDir);

        outputDir.mkdirs();

        File inputFile = new File(sourceDir, relPath);
        File outputFile = new File(output, relPath);

        FileInputStream fis = new FileInputStream(inputFile);
        InputStreamReader isreader = new InputStreamReader(fis, "utf-8");
        BufferedReader breader = new BufferedReader(isreader);

        FileOutputStream fos = new FileOutputStream(outputFile);
        OutputStreamWriter oswriter = new OutputStreamWriter(fos, "utf-8");
        BufferedWriter bwriter = new BufferedWriter(oswriter);

        String line = null;

        nextLine: while ((line = breader.readLine()) != null) {

            int i = 0;
            for (Pattern p : patterns) {

                if (p.matcher(line).matches()) {

                    if (i == 0) {

                        int open = line.split("\\(").length;
                        int close = (line + " ").split("\\)").length;

                        // find closing brackets on following lines.
                        while (open != close && (line = breader.readLine()) != null) {

                            open += line.split("\\(").length;
                            close += (line + " ").split("\\)").length;

                        }

                    }

                    continue nextLine;

                }

                i++;

            }

            bwriter.write(line + "\n");

        }

        bwriter.flush();
        oswriter.close();
        bwriter.close();
        fos.close();
        breader.close();
        isreader.close();
        fis.close();

    }

    // XXX Make configurable.
    // important that annotation is first pattern
    private final static Pattern[] patterns = new Pattern[] {Pattern.compile("[\\s]*@[\\w]+.*"),
            Pattern.compile("[\\s]*import[\\s]+javax.[xml|persistence|annotation].*")};

    @SuppressWarnings("unchecked")
    @Override
    protected List getResources(MavenProject p) throws MojoExecutionException {

        return Collections.EMPTY_LIST;

    }

    @Override
    protected String getClassifier() {
        return "gwtsources";
    }

}
